package lion.dev.encrypt;

import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import org.apache.commons.codec.Charsets;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;

/**
 * @author lion
 * @mail hl_0074@sina.com
 * @date 2011-12-28
 * @des DSA数字签名算法，原理为： 1,生成密钥对，甲方使用私钥对数据进行签名， 2,乙方使用公钥对数据进行验证
 * @eg:
 */
public class DSA {

	public static final String ALGORITHM = "DSA";
	private static final int KEY_SIZE = 1024;
	private PublicKey publicKey;
	private PrivateKey privateKey;

	public void generateKey(String seed) {

		KeyPairGenerator keygen = null;
		try {
			keygen = KeyPairGenerator.getInstance(ALGORITHM);
		} catch (NoSuchAlgorithmException e) {
			throw new IllegalArgumentException("", e);
		}
		SecureRandom secureRandom = null;

		if (StringUtils.isNotBlank(seed)) {
			secureRandom = new SecureRandom(seed.getBytes());
		} else {
			secureRandom = new SecureRandom();
		}
		keygen.initialize(KEY_SIZE, secureRandom);

		KeyPair keys = keygen.genKeyPair();

		publicKey = keys.getPublic();
		privateKey = keys.getPrivate();
	}

	public void generateKey() {

		this.generateKey(null);
	}

	public String getPublicKey() {

		return Base64.encodeBase64String(publicKey.getEncoded());
	}

	public String getPrivateKey() {

		return Base64.encodeBase64String(privateKey.getEncoded());
	}

	/**
	 * 签名数据，反回签名值
	 */
	public byte[] sign(byte[] data, String key) {

		try {
			PrivateKey priKey = parsePrivateKey(key);

			// 用私钥对信息生成数字签名
			Signature signature = Signature.getInstance(ALGORITHM);
			signature.initSign(priKey);
			signature.update(data);

			return signature.sign();
		} catch (Exception e) {
			e.printStackTrace();
		}

		return null;
	}

	/**
	 * 签名字符串，反回签名值
	 */
	public byte[] sign(String data, String key) {

		return sign(data.getBytes(Charsets.UTF_8), key);
	}

	/**
	 * 签名数据，反回base64编码
	 */
	public String signString(byte[] data, String key) {

		return Base64.encodeBase64String(sign(data, key));
	}

	/**
	 * 签名字符串，反回base64编码
	 */
	public String signString(String data, String key) {

		return Base64.encodeBase64String(sign(data, key));
	}

	/**
	 * 验证签名 sign为base64编码字符串
	 */

	public boolean verify(byte[] data, String sign, String pubkey) {

		try {
			// 取公钥匙对象
			PublicKey publicKey = parsePublicKey(pubkey);

			Signature signature = Signature.getInstance(ALGORITHM);
			signature.initVerify(publicKey);
			signature.update(data);
			return signature.verify(Base64.decodeBase64(sign));
		} catch (Exception e) {
			e.printStackTrace();
		}

		// 验证签名是否正常
		return false;

	}

	private PublicKey parsePublicKey(String key) {

		byte[] keyBytes = Base64.decodeBase64(key);

		// 构造X509EncodedKeySpec对象
		X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);

		// ALGORITHM 指定的加密算法
		KeyFactory keyFactory;
		try {
			keyFactory = KeyFactory.getInstance(ALGORITHM);
			return keyFactory.generatePublic(keySpec);
		} catch (Exception e) {
			e.printStackTrace();
		}
		// 取公钥匙对象
		return null;

	}

	private PrivateKey parsePrivateKey(String key) {

		byte[] keyBytes = Base64.decodeBase64(key);

		PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
		try {
			KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
			return keyFactory.generatePrivate(pkcs8KeySpec);
		} catch (Exception e) {
			e.printStackTrace();
		}

		return null;

	}

	public static void main(String[] args) {

		String src = "中华人民共和国";
		DSA dsa = new DSA();
		dsa.generateKey("helin");

		String pubKey = dsa.getPublicKey();
		String privateKey = dsa.getPrivateKey();
		System.out.println("公钥：" + pubKey);
		System.out.println("私钥：" + privateKey);

		String sign = Base64.encodeBase64String(dsa.sign(src.getBytes(), privateKey));
		System.out.println("签名：" + sign);
		System.out.println("验证：" + dsa.verify(src.getBytes(), sign, pubKey));
	}

}
